﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing.Design;
using System.Windows.Forms.Design;
using System.Windows.Forms;
using ProgNetComponents.Collections;
using ProgNetComponents.Classes;
using System.Drawing;
using ProgNetComponents.Interfaces;
using System.ComponentModel.Design;
using System.ComponentModel;
using System.Reflection;

namespace ProgNetComponents.Design.Editors
{
    /// <summary>
    /// Editor for GroupItemName
    /// </summary>
    public abstract class BaseGroupItemNameEditor : UITypeEditor
    {
        /// <summary>
        /// Color for group items
        /// </summary>
        public static Color GroupColor
        {
            get { return Color.Aqua; ; }
        }
        /// <summary>
        /// Color for non Group items
        /// </summary>
        public static Color ItemColor
        {
            get { return Color.White; }
        }
        /// <summary>
        /// IWindowsFormsEditorService
        /// </summary>
        protected IWindowsFormsEditorService editorService;
        /// <summary>
        /// TreeView
        /// </summary>
        protected TreeView dropDownControl = null;
        /// <summary>
        /// AlwaysRefreshItems
        /// </summary>
        protected virtual bool AlwaysRefreshItems
        {
            get { return false; }
        }
        /// <summary>
        /// Collection of items
        /// </summary>
        protected TypeObjectGroupCollection collection = new TypeObjectGroupCollection();
        internal TypeObjectGroupCollection Collection
        {
            get { return collection; }
        }
        /// <summary>
        /// Base type for items to display on tree
        /// </summary>
        protected abstract Type BaseType
        {
            get;
        }
        /// <summary>
        /// Additional Service Provider
        /// </summary>
        protected virtual IServiceProvider AdditionalProvider
        {
            get { return null; }
        }
        /// <summary>
        /// Gets Control Width
        /// </summary>
        protected virtual int ControlWidth
        {
            get { return 350; }
        }
        /// <summary>
        /// Gets Controls Height
        /// </summary>
        protected virtual int ControlHeight
        {
            get { return 300; }
        }
        /// <summary>
        /// Gets if dropdown can be resized
        /// </summary>
        public override bool IsDropDownResizable
        {
            get
            {
                return true;
            }
        }
        /// <summary>
        /// Creates TreeView
        /// </summary>
        /// <param name="context"></param>
        /// <param name="provider"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        protected virtual TreeView CreateControl(System.ComponentModel.ITypeDescriptorContext context, IServiceProvider provider, object value)
        {
            TreeView control  = new TreeView();
            control.BorderStyle = BorderStyle.None;
            control.Width = ControlWidth;
            control.Height = ControlHeight;
            control.DoubleClick += new EventHandler(control_DoubleClick);
            control.KeyDown += new KeyEventHandler(control_KeyDown);
            return control;
        }
        /// <summary>
        /// Fills collection with items
        /// </summary>
        /// <param name="collection"></param>
        /// <param name="context"></param>
        /// <param name="provider"></param>
        /// <param name="value"></param>
        protected virtual void FillCollection(TypeObjectGroupCollection collection, System.ComponentModel.ITypeDescriptorContext context, IServiceProvider provider, object value)
        {
            if (BaseType != null)
            {
                Type[] types = provider.GetTypes(BaseType, true);
                if (types.Length == 0 || (types.Length == 1 && types[0].FullName == BaseType.FullName))
                {
                    object o = context.Instance.GetFieldValue("_Provider");
                    if (o != null)
                    {
                        o = o.GetPropertyValue("Container");
                        if (o != null)
                        {
                            if (o is IServiceProvider)
                            {
                                types = (o as IServiceProvider).GetTypes(BaseType, true);
                            }
                        }
                    }
                }
                if (types.Length == 0 || (types.Length == 1 && types[0].FullName == BaseType.FullName))
                {
                    if (AdditionalProvider != null)
                    {
                        types = AdditionalProvider.GetTypes(BaseType, true);
                    }
                }
                foreach (Type t in types)
                {
                    if (t != BaseType && t.FullName != BaseType.FullName)
                        AddGroup(t, null);
                }
            }
        }
        /// <summary>
        /// Creates nodes on tree
        /// </summary>
        protected virtual void CreateNodes()
        {
            dropDownControl.Nodes.Clear();
            AddItemNode(null, new NamedObject("<null>", null));
            foreach (TypeObjectGroup o in collection)
            {
                AddGroupNode(null, o);
            }
        }
        private void AddEnum(Type t, TypeObjectGroup parent)
        {
            Array a = t.GetEnumValues();
            NamedObject o2;
            TypeObjectGroup group = null;
            if (parent.Groups.Contains(t.Name))
                group = parent.Groups[t.Name];
            else
            {
                group = new TypeObjectGroup(t);
                parent.Groups.Add(group);
            }
            foreach (object i in a)
            {
                o2 = new NamedObject(i.AsString(), i);
                group.Items.Add(o2);
            }
        }
        private void AddGroup(Type t, TypeObjectGroup parent)
        {
            TypeObjectGroup o = new TypeObjectGroup(t);
            if(parent!=null)
                parent.Groups.Add(o);
            else
                collection.Add(o);

            Type[] nested = t.GetNestedTypes();
            foreach (Type n in nested)
            {
                if (!n.IsEnum)
                {
                    AddGroup(n, o);
                }
            }
            foreach (Type n in nested)
            {
                if (n.IsEnum)
                {
                    AddEnum(n, o);
                }
            }
        }
        /// <summary>
        /// Closes dropdown
        /// </summary>
        protected void CloseDropDown()
        {
            if (dropDownControl.SelectedNode != null)
            {
                if (dropDownControl.SelectedNode.Tag is Enum || dropDownControl.SelectedNode.Tag == null)
                {
                    if (editorService != null)
                        editorService.CloseDropDown();
                }
            }
        }
        /// <summary>
        /// Edits value
        /// </summary>
        /// <param name="context"></param>
        /// <param name="provider"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public override object EditValue(System.ComponentModel.ITypeDescriptorContext context, IServiceProvider provider, object value)
        {
            editorService = provider.GetService<IWindowsFormsEditorService>();

            if (editorService != null)
            {
                if (dropDownControl == null)
                {
                    dropDownControl = CreateControl(context, provider, value);
                }
                if (AlwaysRefreshItems || collection.Count < 1)
                {
                    collection.Clear();
                    FillCollection(collection, context, provider, value);

                    CreateNodes();
                }
                dropDownControl.CollapseAll();
                SelectNode(dropDownControl.Nodes, value);
                editorService.DropDownControl(dropDownControl);
                if (dropDownControl.SelectedNode != null)
                {
                    if (dropDownControl.SelectedNode.Tag == null || dropDownControl.SelectedNode.Tag is Enum)
                        value = dropDownControl.SelectedNode.Tag;
                }
                return value;
            }
            return null;
        }
        /// <summary>
        /// Adds Group node
        /// </summary>
        /// <param name="parent"></param>
        /// <param name="g"></param>
        /// <returns></returns>
        protected virtual TreeNode AddGroupNode(TreeNode parent, TypeObjectGroup g )
        {
            TreeNodeCollection nodes = parent == null ? dropDownControl.Nodes : parent.Nodes;
            TreeNode n = new TreeNode(g.Name.XReplace("_Group$",""));
            n.Tag = g.Type;
            nodes.Add(n);
            n.BackColor = GroupColor;
            foreach (TypeObjectGroup o2 in g.Groups)
                AddGroupNode(n, o2);
            foreach (NamedObject o2 in g.Items)
                AddItemNode(n, o2);
            return n;
        }
        /// <summary>
        /// Adds item node
        /// </summary>
        /// <param name="parent"></param>
        /// <param name="g"></param>
        /// <returns></returns>
        protected virtual TreeNode AddItemNode(TreeNode parent, NamedObject g)
        {
            TreeNodeCollection nodes = parent == null ? dropDownControl.Nodes : parent.Nodes;
            TreeNode n = new TreeNode(g.Name);
            n.Tag = g.Value;
            n.BackColor = ItemColor;
            nodes.Add(n);
            return n;
        }
        private void control_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
                CloseDropDown();
        }

        private void control_DoubleClick(object sender, EventArgs e)
        {
            CloseDropDown();
        }
        /// <summary>
        /// Gets edit style
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public override UITypeEditorEditStyle GetEditStyle(System.ComponentModel.ITypeDescriptorContext context)
        {
            return UITypeEditorEditStyle.DropDown;
        }

        private bool SelectNode(TreeNodeCollection nodes, object value)
        {
            foreach (TreeNode n in nodes)
            {
                if (n.Tag.EqualsSafe(value))
                {
                    SetExpanded(n);
                    dropDownControl.SelectedNode = n;
                    return true;
                }
                if (SelectNode(n.Nodes, value))
                {
                    return true;
                }
            }
            return false;
        }

        private void SetExpanded(TreeNode n)
        {
            if (n.Parent != null)
            {
                SetExpanded(n.Parent);
                n.Parent.Expand();
            }
        }
    }
    /// <summary>
    /// BaseGroupItemNameEditor 
    /// </summary>
    /// <typeparam name="T">Type for which groups and items will be read</typeparam>
    public class BaseGroupItemNameEditor<T> : BaseGroupItemNameEditor
    {
        /// <summary>
        /// Type
        /// </summary>
        protected override Type BaseType
        {
            get { return typeof(T); }
        }
    }
    /// <summary>
    /// TypeObjectGroup 
    /// </summary>
    public class TypeObjectGroup : Interfaces.INamedGroup
    {
        /// <summary>
        /// Creates TypeObjectGroup instance
        /// </summary>
        /// <param name="t"></param>
        public TypeObjectGroup(Type t)
        {
            this.Type = t;
            this.Name = t.Name.XReplace("_Group$", "");
        }
        private string _Name;
        private TypeObjectGroupCollection _Groups = new TypeObjectGroupCollection();
        private NamedObjectCollection _Items = new NamedObjectCollection();
        /// <summary>
        /// Type
        /// </summary>
        public Type Type
        {
            get;
            set;
        }

        #region INamedGroup Members
        /// <summary>
        /// Child Groups
        /// </summary>
        public TypeObjectGroupCollection Groups
        {
            get { return _Groups; }
        }
        /// <summary>
        /// Items
        /// </summary>
        public NamedObjectCollection Items
        {
            get { return _Items; }
        }

        #endregion

        #region INamedObject Members
        /// <summary>
        /// Name
        /// </summary>
        public string Name
        {
            get
            {
                return _Name;
            }
            set
            {
                _Name = Tools.ConstHelper.GetFriendlyName(value);
            }
        }

        #endregion

        #region INamedGroup Members

        System.Collections.IList Interfaces.INamedGroup.Groups
        {
            get
            {
                return _Groups;
            }
        }

        System.Collections.IList Interfaces.INamedGroup.Items
        {
            get { return _Items; }
        }

        #endregion

        #region INamedGroup Members

        bool Interfaces.INamedGroup.HasAnyItems()
        {
            if (Items.Count > 0)
                return true;
            foreach (INamedGroup g in Groups)
                if (g.HasAnyItems())
                    return true;
            return false;
        }

        #endregion

        
    }
    /// <summary>
    /// TypeObjectGroupCollection
    /// </summary>
    public class TypeObjectGroupCollection : Collections.BaseNamedGroupCollection<TypeObjectGroup>
    {

    }
}
